﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CounterStrikeSourceAimbot;

namespace TempUI
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this.Context = new MainWindowDataContext();
            
            this.Context
        }

        public MainWindowDataContext Context;

        private void button1_Click(object sender, RoutedEventArgs e)
        {

            var ma = new[] {-1246, -232, -160};
            var targ = new[] {-699, 142, -438};


            var deltax = targ[0] - ma[0];
            var deltay = targ[1] - ma[1];
            var deltaZ = targ[2] - ma[2];

            var vec = MathUtil.GetPitchYaw(new double[] {deltax, deltay, deltaZ});
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            var aimBot = new AimBot(Context.AvailablePlayers.ToList(), Context.AvailablePlayers.First());

            aimBot.BindAsync();
            //aimBot.FocusOnPlayer(Context.AvailablePlayers.First(), Context.AvailablePlayers.Skip(1).First());

            Keyboard.IsKeyDown(Key.K);
        }
    }

    public class MainWindowDataContext : INotifyPropertyChanged
    {
        public MainWindowDataContext()
        {
            this.AvailablePlayers =
                new ObservableCollection<PlayerData>(
                    new List<PlayerData>(Enumerable.Repeat(0, 32).Select(i => new PlayerData())));

            var prc = System.Diagnostics.Process.GetProcessesByName("hl2");

            if (prc.Length == 0)
            {
                Console.WriteLine("hl2.exe not found!");
                //Console.ReadKey();
                return;
            }

            if (prc.Length > 1)
            {
                Console.WriteLine("multiple hl2.exe found!");
                //Console.ReadKey();
                return;
            }

            Engine = new MemoryEngine(prc.First());

            Engine.PlayerListStartAddress = engine.FindPlayerListAddress("epsi1on");

            var tmr = new Timer(RefreshInterval == 0 ? 100 : RefreshInterval);

            tmr.Elapsed += TmrOnElapsed;

            tmr.Start();

            this.SelectedPlayersDatas=new ObservableCollection<PlayerData>();


            var tmr2 = new Timer(100);
            tmr2.Elapsed += (sender, args) => RecalcNeededYawPitch();
            tmr2.Start();
            
        }

        private void TmrOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            RefreshPlayerData();
        }

        private void RefreshPlayerData()
        {
            engine.RefreshClients(availablePlayers.ToArray());
        }

        #region INotifyPropertyChanged members and helpers

        public event PropertyChangedEventHandler PropertyChanged;

        private static bool AreEqualObjects(object obj1, object obj2)
        {
            var obj1Null = ReferenceEquals(obj1, null);
            var obj2Null = ReferenceEquals(obj2, null);

            if (obj1Null && obj2Null)
                return true;

            if (obj1Null || obj2Null)
                return false;

            if (obj1.GetType() != obj2.GetType())
                return false;

            if (ReferenceEquals(obj1, obj2))
                return true;

            return obj1.Equals(obj2);
        }

        private void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                return;

            if (this.PropertyChanged != null)
                foreach (var propertyName in propertyNames)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region RefreshInterval Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public int RefreshInterval
        {
            get { return refreshInterval; }
            set
            {
                if (AreEqualObjects(refreshInterval, value))
                    return;

                var _fieldOldValue = refreshInterval;

                refreshInterval = value;

                MainWindowDataContext.OnRefreshIntervalChanged(this,
                    new PropertyValueChangedEventArgs<int>(_fieldOldValue, value));

                this.OnPropertyChanged("RefreshInterval");
            }
        }

        private int refreshInterval;

        public EventHandler<PropertyValueChangedEventArgs<int>> RefreshIntervalChanged;

        public static void OnRefreshIntervalChanged(object sender, PropertyValueChangedEventArgs<int> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.RefreshIntervalChanged != null)
                obj.RefreshIntervalChanged(obj, e);
        }

        #endregion

        #region Engine Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public MemoryEngine Engine
        {
            get { return engine; }
            set
            {
                if (AreEqualObjects(engine, value))
                    return;

                var _fieldOldValue = engine;

                engine = value;

                MainWindowDataContext.OnEngineChanged(this,
                    new PropertyValueChangedEventArgs<MemoryEngine>(_fieldOldValue, value));

                this.OnPropertyChanged("Engine");
            }
        }

        private MemoryEngine engine;

        public EventHandler<PropertyValueChangedEventArgs<MemoryEngine>> EngineChanged;

        public static void OnEngineChanged(object sender, PropertyValueChangedEventArgs<MemoryEngine> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.EngineChanged != null)
                obj.EngineChanged(obj, e);
        }

        #endregion

        #region AvailablePlayers Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public ObservableCollection<PlayerData> AvailablePlayers
        {
            get { return availablePlayers; }
            set
            {
                if (AreEqualObjects(availablePlayers, value))
                    return;

                var _fieldOldValue = availablePlayers;

                availablePlayers = value;

                MainWindowDataContext.OnAvailablePlayersChanged(this,
                    new PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>(_fieldOldValue, value));

                this.OnPropertyChanged("AvailablePlayers");
            }
        }

        private ObservableCollection<PlayerData> availablePlayers;

        public EventHandler<PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>> AvailablePlayersChanged;

        public static void OnAvailablePlayersChanged(object sender,
            PropertyValueChangedEventArgs<ObservableCollection<PlayerData>> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.AvailablePlayersChanged != null)
                obj.AvailablePlayersChanged(obj, e);
        }

        #endregion

        #region SelectedPlayerData Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public PlayerData SelectedPlayerData
        {
            get { return selectedPlayerData; }
            set
            {
                if (AreEqualObjects(selectedPlayerData, value))
                    return;

                var _fieldOldValue = selectedPlayerData;

                selectedPlayerData = value;

                MainWindowDataContext.OnSelectedPlayerDataChanged(this,
                    new PropertyValueChangedEventArgs<PlayerData>(_fieldOldValue, value));

                this.OnPropertyChanged("SelectedPlayerData");
            }
        }

        private PlayerData selectedPlayerData;

        public EventHandler<PropertyValueChangedEventArgs<PlayerData>> SelectedPlayerDataChanged;

        public static void OnSelectedPlayerDataChanged(object sender, PropertyValueChangedEventArgs<PlayerData> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.SelectedPlayerDataChanged != null)
                obj.SelectedPlayerDataChanged(obj, e);

            obj.SelectedPlayersDatas.Clear();
            obj.SelectedPlayersDatas.Add(e.NewValue);
        }

        #endregion

        #region SelectedPlayersDatas Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public ObservableCollection<PlayerData> SelectedPlayersDatas
        {
            get { return selectedPlayersDatas; }
            set
            {
                if (AreEqualObjects(selectedPlayersDatas, value))
                    return;

                var _fieldOldValue = selectedPlayersDatas;

                selectedPlayersDatas = value;

                MainWindowDataContext.OnSelectedPlayersDatasChanged(this,
                    new PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>(_fieldOldValue, value));

                this.OnPropertyChanged("SelectedPlayersDatas");
            }
        }

        private ObservableCollection<PlayerData> selectedPlayersDatas;

        public EventHandler<PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>> SelectedPlayersDatasChanged;

        public static void OnSelectedPlayersDatasChanged(object sender,
            PropertyValueChangedEventArgs<ObservableCollection<PlayerData>> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.SelectedPlayersDatasChanged != null)
                obj.SelectedPlayersDatasChanged(obj, e);
        }

        #endregion

        #region NeededYaw Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double NeededYaw
        {
            get { return neededYaw; }
            set
            {
                if (AreEqualObjects(neededYaw, value))
                    return;

                var _fieldOldValue = neededYaw;

                neededYaw = value;

                MainWindowDataContext.OnNeededYawChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("NeededYaw");
            }
        }

        private double neededYaw;

        public EventHandler<PropertyValueChangedEventArgs<double>> NeededYawChanged;

        public static void OnNeededYawChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.NeededYawChanged != null)
                obj.NeededYawChanged(obj, e);


        }

        #endregion

        #region NeedePitch Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double NeedePitch
        {
            get { return needePitch; }
            set
            {
                if (AreEqualObjects(needePitch, value))
                    return;

                var _fieldOldValue = needePitch;

                needePitch = value;

                MainWindowDataContext.OnNeedePitchChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("NeedePitch");
            }
        }

        private double needePitch;

        public EventHandler<PropertyValueChangedEventArgs<double>> NeedePitchChanged;

        public static void OnNeedePitchChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.NeedePitchChanged != null)
                obj.NeedePitchChanged(obj, e);


        }

        #endregion

        #region Landa Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Landa
        {
            get { return landa; }
            set
            {
                if (AreEqualObjects(landa, value))
                    return;

                var _fieldOldValue = landa;

                landa = value;

                MainWindowDataContext.OnLandaChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Landa");
            }
        }

        private double landa;

        public EventHandler<PropertyValueChangedEventArgs<double>> LandaChanged;

        public static void OnLandaChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.LandaChanged != null)
                obj.LandaChanged(obj, e);


        }

        #endregion
        private void RecalcNeededYawPitch()
        {
            var Local = availablePlayers.First();
            var Target = availablePlayers.Skip(1).First();

            var delatX = Target.X - Local.X;
            var delatY = Target.Y - Local.Y;
            var delatZ = Target.Z - Local.Z;

            var direction = MathUtil.GetPitchYaw(delatX, delatY, delatZ);

            NeededYaw = Local.Yaw - direction[0];
            NeedePitch = Local.Pitch - direction[1];

            Landa = MathUtil.GetLanda(NeededYaw, NeedePitch);
        }
    }
}